""" Basic Poll Example for Fontys SOT Course

This example is the basis for your exploration in Service Oriented Technology. We will make many changes.

Changes:
1.0.0: This version uses HTTP GET and will reply with XML responses.
2.0.0: This version uses XML-RPC and will reply with XML-RPC responses, but cannot yet vote or get results.
2.1.0: TODO: This version adds vote and get_result to the API.
"""
import logging
from flask import Flask
from flask import abort
from f import FastRPCHandler

logging.basicConfig(level=logging.INFO)

app = Flask(__name__)

__AUTHOR__ = "Caixia & Roy Berkeveld"  # Change to your name if you like.
__VERSION__ = "2.0.0"  # Check out Semantic Versioning (semver.org) to decide how to change the version.
__MAJOR__ = __VERSION__.split(".")[0]  # We use this in the API URL. Major versions are not backwards-compatible.


# First we define the data model of our application.
# This is currently stored in process memory, which is fine for static data.

class Poll(object):
    """ The data structure used to represent a single poll """

    def __init__(self, poll_id, question):
        self.poll_id = poll_id
        self.question = question
        self.options = dict()
        self.votes = set()  # A set cannot contain duplicates

    def add_option(self, description):
        option_id = max(self.options.keys(), default=0) + 1
        self.options[option_id] = PollOption(option_id, description, parent=self)

    def to_template(self):
        return dict(
            poll_id=self.poll_id,
            question=self.question,
            options=[self.options[option].to_template() for option in self.options])


class PollOption(object):
    """ The data structure used to represent each poll option """

    def __init__(self, option_id, description, parent=None):
        self.option_id = option_id
        self.description = description
        self.parent = parent
        self.votes = set()  # A set cannot contain duplicates

    def vote_count(self):
        return len(self.votes)

    def to_template(self):
        return dict(
            option_id=self.option_id,
            description=self.description,
            vote_count=self.vote_count())


db = dict()  # A dictionary maps keys to values, in this case poll_ids to their class objects.


def add_poll(question):
    """ Adds a new poll to the database """
    poll_id = max(db.keys(), default=0) + 1
    db[poll_id] = Poll(poll_id, question)
    return poll_id


def init_db():
    """ Initialise the database with an example. """
    global db  # Get global db into our local namespace, so we can modify it
    db = dict()  # Re-initialize db
    poll_id = add_poll('What is your CPU Architecture?')
    db[poll_id].add_option('X86 (32-bit OS)')
    db[poll_id].add_option('AMD64 (64-bit PC/Intel Mac)')
    db[poll_id].add_option('ARM64 (Apple Silicon)')


init_db()  # Don't forget to call the setup function!


# For XML-RPC we don't have routes, but an endpoint at the API version prefix.
# Any request to this endpoint handler should be interpreted as XML-RPC and given a similar response.

# Make XML-RPC handle all requests to our API version
poll = FastRPCHandler(app, url='/v'+__MAJOR__)


def index() -> str:
    return ' '.join(map(str, db.keys()))


def get_question(poll_id: int) -> str:
    if poll_id not in db: abort(404, 'Poll not found')
    return db[poll_id].question


def get_options(poll_id: int) -> str:
    if poll_id not in db: abort(404, 'Poll not found')
    return ' '.join(map(str, db[poll_id].options.keys()))


def get_option_description(poll_id: int, option_id: int) -> str:
    if poll_id not in db: abort(404, 'Poll not found')
    if option_id not in db[poll_id].options: abort(404, 'No such option')
    return db[poll_id].options[option_id].description


def vote(poll_id: int, option_id: int, voter: str) -> str:
    raise NotImplementedError  # TODO implement this, check v1.0.0 for a reference


def get_result(poll_id: int, option_id: int) -> int:
    raise NotImplementedError  # TODO implement this, check v1.0.0 for a reference


poll.register_method('index', index)
poll.register_method('get_question', get_question)
poll.register_method('get_options', get_options)
poll.register_method('get_option_description', get_option_description)
poll.register_method('vote', vote)
poll.register_method('get_result', get_result)

# Run the tests in testapp.py and check the communication with Wireshark
